package com.backend.werewolfkillingbackend.handle;


import com.backend.werewolfkillingbackend.model.*;
import com.backend.werewolfkillingbackend.pojo.*;
import com.backend.werewolfkillingbackend.service.*;
import com.backend.werewolfkillingbackend.util.JsonUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


@Component
public class WebSocketHandler extends TextWebSocketHandler {

    private static final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();

    Map<Long, Room> roomMap = new HashMap<>();

    Map<Long, Player> tokenMap = new HashMap<>();

    Map<Long, List<Long>> voteMap = new HashMap<>();

    private static final List<Integer> roleList = new ArrayList<>() {{
        add(1);
        add(1);
        add(2);
        add(2);
        add(3);
        add(4);
    }};

    @Autowired
    private PlayerService playerService;

    @Autowired
    private RoomService roomService;

    @Autowired
    private MessageService messageService;

    @Autowired
    private RoleService roleService;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String sessionId = session.getId();
        String username = session.getAttributes().get("username").toString();
        String roomId = session.getAttributes().get("roomId").toString();
        Room room = roomService.getById(roomId);
        sessions.put(sessionId, session);

        List<Player> players = JsonUtil.parseJsonToList(room.getPlayerList(), Player.class);
        for (Player player : players) {
            if (player.getName().equals(username)) {
                Message message = new Message(JsonUtil.parseObjToJson(room), Long.valueOf(roomId), MessageType.SYSTEM);
                message.setMsg("房间信息");
                sendMessage(sessionId, message);
                return;
            }
        }

        LambdaQueryWrapper<Player> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Player::getName, username);
        Player player = playerService.getOne(queryWrapper);

        player.setName(username);
        player.setRoomId(Long.valueOf(roomId));
        player.setIsRoomOwner(room.getRoomOwner().equals(username) ? 1 : 0);

        players.add(player);
        room.setPlayerList(JsonUtil.parseListToJson(players));

        playerService.updateById(player);
        roomService.updateById(room);
        if (!roomMap.containsKey(Long.valueOf(roomId))) {
            roomMap.put(Long.valueOf(roomId), room);
        }

        broadcastSystemMessage(username + "加入了房间", roomId);

        notifyRoomUpdate(sessionId, JsonUtil.parseObjToJson(room), roomId);

        Message message = new Message(JsonUtil.parseObjToJson(room), Long.valueOf(roomId), MessageType.SYSTEM);
        message.setMsg("房间信息");
        sendMessage(sessionId, message);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String username = session.getAttributes().get("username").toString();
        LambdaQueryWrapper<Player> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Player::getName, username);
        Player player = playerService.getOne(queryWrapper);
        String roomId = session.getAttributes().get("roomId").toString();
        String content = message.getPayload();
        MessageInfo info = JsonUtil.parseJsonToObj(content, MessageInfo.class);
        switch (info.getType()) {
            case "chat" ->
                    broadcastChatMessage(info.getContent(), roomId, String.valueOf(player.getId()), player.getName());
            case "token" -> {
                Token token = JsonUtil.parseJsonToObj(info.getContent(), Token.class);
                Player target = playerService.getById(token.getTarget_id());
                switch (token.getToken_type()) {
                    case "werewolf" -> {
                        if (tokenMap.containsKey(target.getId())) {
                            Player p = tokenMap.get(target.getId());
                            p.setWerewolf_token(p.getWerewolf_token() + 1);
                            tokenMap.put(target.getId(), p);
                            break;
                        }
                        target.setWerewolf_token(target.getWerewolf_token() + 1);
                        tokenMap.put(target.getId(), target);
                    }
                    case "seer" -> {
                        Message m = new Message("好人", Long.valueOf(roomId), MessageType.JUDGE);
                        m.setMsg("查询成功");
                        if (target.getRoleId() == 2) {
                            m.setContent("狼人");
                        }
                        session.sendMessage(new TextMessage(JsonUtil.parseObjToJson(m)));
                    }
                    case "guard" -> {
                        if (tokenMap.containsKey(target.getId())) {
                            Player p = tokenMap.get(target.getId());
                            p.setGuard_token(1);
                            tokenMap.put(target.getId(), p);
                            break;
                        }
                        target.setGuard_token(1);
                        tokenMap.put(target.getId(), target);
                    }
                    case "vote" -> {
                        List<Long> primeList = new ArrayList<>();
                        if (voteMap.containsKey(token.getTarget_id())) {
                            primeList = voteMap.get(target.getId());
                            primeList.add(token.getPrime_id());
                            voteMap.put(token.getTarget_id(), primeList);
                            break;
                        }
                        primeList.add(token.getPrime_id());
                        voteMap.put(token.getTarget_id(), primeList);
                    }
                }
            }
            case "stage" -> {
                Message m = new Message();
                m.setPlayerName("法官");
                m.setMessageType(MessageType.JUDGE);
                m.setRoomId(Long.valueOf(roomId));
                switch (info.getContent()) {
                    case "start" -> {
                        m.setMsg("游戏开始");
                        m.setContent("游戏开始");
                        broadcastGameStart(roomId, JsonUtil.parseJsonToList(roomService.getById(roomId).getPlayerList(), Player.class));
                    }
                    case "night" -> {
                        m.setMsg("黑夜");
                        m.setContent("天黑请闭眼");
                    }
                    case "day" -> {
                        m.setMsg("白天");
                        m.setContent("天亮了");
                    }
                    case "action" -> {
                        m.setMsg("开始行动");
                        m.setContent("狼人、预言家、守卫请开始行动");
                    }
                    case "settle" -> {
                        m.setMsg("settle");
                        SettleResult settleResult = new SettleResult();
                        List<Player> tokenPlayer = new ArrayList<>();
                        for (Player value : tokenMap.values()) {
                            if (value.getWerewolf_token() > 0 && value.getGuard_token() == 0) {
                                tokenPlayer.add(value);
                            }
                        }
                        if (tokenPlayer.size() == 0) {
                            settleResult.setMsg("平安夜");
                            settleResult.setContent("昨天晚上是平安夜");
                            m.setContent(JsonUtil.parseObjToJson(settleResult));
                            break;
                        }
                        Player diedPlayer = tokenPlayer.get(0);
                        diedPlayer.setAlive(0);
                        playerService.updateById(diedPlayer);
                        LambdaQueryWrapper<Player> wrapper = new LambdaQueryWrapper<>();
                        wrapper.eq(Player::getRoomId, roomId);
                        settleResult.setPlayerList(playerService.list(wrapper));
                        settleResult.getPlayerList().sort(Comparator.comparingInt(Player::getSeat));
                        settleResult.setPlayer(diedPlayer);
                        settleResult.setMsg("玩家死亡");
                        settleResult.setContent("玩家死亡");
                        m.setContent(JsonUtil.parseObjToJson(settleResult));
                        tokenMap.clear();
                        int flag = checkGameOver(roomId);
                        if (flag != 0) {
                            notifyGameOver(roomId, flag == 1 ? "游戏结束,好人胜利" : "游戏结束,狼人胜利");
                        }
                    }
                    case "vote" -> {
                        m.setMsg("投票");
                        m.setContent("请开始投票");
                    }
                    case "chat" -> {
                        m.setMsg("发言");
                        m.setContent("请开始自由发言");
                    }
                    case "singleChat" -> {
                        m.setMsg("出局遗言");
                        m.setContent("请发表遗言");
                    }
                    case "diedChat" -> {
                        m.setMsg("被刀遗言");
                        m.setContent("请发表遗言");
                    }
                    case "settleVote" -> {
                        m.setMsg("投票结果");
                        List<Vote> voteList = new ArrayList<>();
                        for (Map.Entry<Long, List<Long>> entry : voteMap.entrySet()) {
                            Vote vote = new Vote(Long.valueOf(roomId), entry.getKey(), entry.getValue());
                            vote.setTarget_seat(playerService.getById(entry.getKey()).getSeat());
                            List<Long> seatList = new ArrayList<>();
                            for (Long id : vote.getInitiator_id()) {
                                Player pi = playerService.getById(id);
                                seatList.add(Long.valueOf(pi.getSeat()));
                            }
                            vote.setInitiator_seat(seatList);

                            voteList.add(vote);
                        }
                        voteMap.clear();
                        SettleResult settleResult = new SettleResult();
                        if (voteList.size() == 0) {
                            settleResult.setContent("投票结束");
                            m.setContent(JsonUtil.parseObjToJson(settleResult));
                            break;
                        }
                        int max = 0;
                        List<Vote> maxVoteList = new ArrayList<>();
                        for (Vote vote : voteList) {
                            if (vote.getInitiator_id().size() > max) {
                                max = vote.getInitiator_id().size();
                            }
                        }
                        for (Vote vote : voteList) {
                            if (vote.getInitiator_id().size() == max) {
                                maxVoteList.add(vote);
                            }
                        }
                        Player votedPlayer = playerService.getById(maxVoteList.get(0).getTarget_id());
                        votedPlayer.setAlive(0);
                        playerService.updateById(votedPlayer);
                        settleResult.setPlayer(votedPlayer);
                        settleResult.setVoteList(voteList);
                        m.setContent(JsonUtil.parseObjToJson(settleResult));
                        int flag = checkGameOver(roomId);
                        if (flag != 0) {
                            notifyGameOver(roomId, flag == 1 ? "游戏结束,好人胜利" : "游戏结束,狼人胜利");
                        }
                    }
                }
                broadcastJudgeMessage(m);
            }
            case "teamChat" -> {
                for (WebSocketSession socketSession : findWerewolfSession(roomId)) {
                    Message m = new Message();
                    m.setRoomId(Long.valueOf(roomId));
                    m.setMessageType(MessageType.CHAT);
                    m.setContent(info.getContent());
                    m.setMsg("队内聊天");
                    m.setPlayerName(player.getName());
                    m.setPlayer(player);
                    socketSession.sendMessage(new TextMessage(JsonUtil.parseObjToJson(m)));
                }
            }
            case "teammate" -> {
                Message m = new Message();
                m.setMessageType(MessageType.JUDGE);
                m.setMsg("队友");
                m.setRoomId(Long.valueOf(roomId));
                Room room = roomMap.get(Long.valueOf(roomId));
                List<Player> playerList = JsonUtil.parseJsonToList(room.getPlayerList(), Player.class);
                List<Player> list = playerList.stream().filter(p -> p.getRoleId() == 2).toList();
                m.setContent(JsonUtil.parseListToJson(list));
                for (WebSocketSession socketSession : findWerewolfSession(roomId)) {
                    socketSession.sendMessage(new TextMessage(JsonUtil.parseObjToJson(m)));
                }
            }
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String sessionId = session.getId();
        String roomId = session.getAttributes().get("roomId").toString();
        String username = session.getAttributes().get("username").toString();
        Room room = roomService.getById(roomId);
        List<Player> players = JsonUtil.parseJsonToList(room.getPlayerList(), Player.class);
        players.removeIf(player -> player.getName().equals(username));
        room.setPlayerList(JsonUtil.parseListToJson(players));
        notifyRoomUpdate(sessionId, JsonUtil.parseObjToJson(room), roomId);
        sessions.remove(sessionId);
    }

    private void sendMessage(String sessionId, Message message) {
        WebSocketSession session = sessions.get(sessionId);
        if (session != null && session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(JsonUtil.parseObjToJson(message)));
            } catch (IOException e) {
                // 处理消息发送异常
            }
        }
    }

    private void notifyRoomUpdate(String sessionId, String message, String roomId) throws IOException {
        WebSocketSession session = sessions.get(sessionId);
        Message m = new Message(message, Long.valueOf(roomId), MessageType.SYSTEM);
        m.setPlayerName("系统");
        m.setMsg("房间信息");
        if (session != null && session.isOpen()) {
            findRoomSession(roomId).stream().filter(s -> !s.getId().equals(sessionId)).forEach(s -> {
                try {
                    s.sendMessage(new TextMessage(JsonUtil.parseObjToJson(m)));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }

    private void notifyGameOver(String roomId, String whoWin) throws IOException {
        for (WebSocketSession socketSession : findRoomSession(roomId)) {
            Message over = new Message(whoWin, Long.valueOf(roomId), MessageType.JUDGE);
            over.setMsg("游戏结束");
            over.setPlayerName("法官");
            socketSession.sendMessage(new TextMessage(JsonUtil.parseObjToJson(over)));
        }
    }

    private void broadcastSystemMessage(String content, String roomId) throws IOException {
        Message message = new Message(content, Long.valueOf(roomId), MessageType.SYSTEM);
        message.setPlayerName("系统");
        message.setMsg("系统消息");
        findRoomSession(roomId).forEach(s -> {
            try {
                s.sendMessage(new TextMessage(JsonUtil.parseObjToJson(message)));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }

    public void broadcastJudgeMessage(Message message) {
        sessions.values().forEach(s -> {
            try {
                s.sendMessage(new TextMessage(JsonUtil.parseObjToJson(message)));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
        messageService.save(message);
    }

    private void broadcastChatMessage(String content, String roomId, String playerId, String playerName) throws IOException {
        Message message = new Message(content, Long.valueOf(roomId), Long.valueOf(playerId), playerName, MessageType.CHAT);
        message.setPlayer(playerService.getById(playerId));
        findRoomSession(roomId).forEach(s -> {
            try {
                s.sendMessage(new TextMessage(JsonUtil.parseObjToJson(message)));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
        messageService.save(message);
    }

    public void broadcastGameStart(String roomId, List<Player> players) throws IOException {
        List<Player> playerList = assignRoleToPlayer(players);
        int count = 1;
        for (Player player : playerList) {
            player.setSeat(count);
            playerService.updateById(player);
            WebSocketSession webSocketSession = findPlayer(player.getName());
            Message message = new Message(JsonUtil.parseObjToJson(player), Long.valueOf(roomId), MessageType.SYSTEM);
            message.setMsg("role");
            webSocketSession.sendMessage(new TextMessage(JsonUtil.parseObjToJson(message)));
            count++;
        }
        roomMap.get(Long.valueOf(roomId)).setPlayerList(JsonUtil.parseListToJson(playerList));
    }

    public List<Player> assignRoleToPlayer(List<Player> playerList) {
        Collections.shuffle(roleList);
        for (int i = 0; i < playerList.size(); i++) {
            Player player = playerList.get(i);
            player.setRoleId(Long.valueOf(roleList.get(i)));
            player.setRole(roleService.getById(roleList.get(i)));
        }

        return playerList;
    }

    private WebSocketSession findPlayer(String playerName) {
        Optional<WebSocketSession> optional = sessions.values().stream().filter(s -> s.getAttributes().get("username").equals(playerName)).findFirst();
        return optional.orElse(null);
    }

    private List<WebSocketSession> findRoomSession(String roomId) throws IOException {
        Room room = roomService.getById(roomId);
        List<Player> playerList = JsonUtil.parseJsonToList(room.getPlayerList(), Player.class);
        List<WebSocketSession> sessionList = new ArrayList<>();
        for (Player player : playerList) {
            sessionList.add(findPlayer(player.getName()));
        }
        return sessionList;
    }

    private List<WebSocketSession> findWerewolfSession(String roomId) throws IOException {
        Room room = roomMap.get(Long.valueOf(roomId));
        List<Player> playerList = JsonUtil.parseJsonToList(room.getPlayerList(), Player.class);
        List<WebSocketSession> wolfSession = new ArrayList<>();
        for (Player player : playerList) {
            if (player.getRoleId() == 2) {
                wolfSession.add(findPlayer(player.getName()));
            }
        }
        return wolfSession;
    }

    private Integer checkGameOver(String roomId) {
        LambdaQueryWrapper<Player> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Player::getRoomId, roomId);
        List<Player> playerList = playerService.list(queryWrapper);
        int shen = 0;
        int cun = 0;
        int wolf = 0;
        for (Player player : playerList) {
            if (player.getRoleId() == 1 && player.getAlive() == 1) {
                cun++;
                continue;
            }
            if (player.getRoleId() == 2 && player.getAlive() == 1) {
                wolf++;
                continue;
            }
            if (player.getAlive() == 1) {
                shen++;
            }
        }
        if (shen == 0 || cun == 0) {
            return 2;
        }
        if (wolf == 0) {
            return 1;
        }
        return 0;
    }

}

